﻿using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CadDotNetTools
{
    public static class EntityTools
    {
        /// <summary>
        /// 通过ObjectId得到实体
        /// </summary>
        /// <param name="objIds"></param>
        /// <param name="db"></param>
        /// <returns></returns>
        public static Entity[] ToObjectIdGetEntity(this Database db, params ObjectId[] objIds)
        {

            Entity[] ents = new Entity[objIds.Length];
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                for (int i = 0; i < objIds.Length; i++)
                {
                    DBObject dBObj = objIds[i].GetObject(OpenMode.ForRead);
                    ents[i] = (Entity)dBObj;
                }
            }
            return ents;
        }
        /// <summary>
        /// 通过ObjectId得到实体
        /// </summary>
        /// <param name="objId"></param>
        /// <param name="db"></param>
        /// <returns></returns>
        public static Entity ToObjectIdGetEntity(this Database db, ObjectId objId)
        {

            Entity ent = null;
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {

                DBObject dBObj = objId.GetObject(OpenMode.ForRead);
                ent = (Entity)dBObj;

            }
            return ent;
        }

        /// <summary>
        /// 移动实体
        /// </summary>
        /// <param name="id">实体的ObjectId</param>
        /// <param name="sourcept">移动源点</param>
        /// <param name="targetpt">移动的目标点</param>
        public static void Move(this ObjectId id, Point3d sourcePt, Point3d targetPt)
        {
            //构建用于移动实体的矩阵
            var vector = sourcePt.GetVectorTo(targetPt);
            var mt = Matrix3d.Displacement(vector);
            //以写的方式打开id表示的实体对象
            var ent = id.GetObject(OpenMode.ForWrite) as Entity;
            //对实体实施移动
            ent.TransformBy(mt);
            //为防止错误,切换实体为读的状态
            ent.DowngradeOpen();
        }

        public static void Move(this Entity ent, Point3d sourcePt, Point3d targetPt)
        {
            if (ent.IsNewObject)  //如果是还未被添加到数据库中的新实体
            {
                //构造用于移动实体的矩阵
                var vector = targetPt.GetVectorTo(sourcePt);
                var mt = Matrix3d.Displacement(vector);
                ent.TransformBy(mt);  //对实体实施移动
            }
            else  //如果是已经够添加到书记库中的实体
            {
                ent.ObjectId.Move(sourcePt, targetPt);
            }
        }
        /// <summary>
        /// 复制
        /// </summary>
        /// <param name="id">实体的ObjectId</param>
        /// <param name="sourcePt">复制的源点</param>
        /// <param name="targetPt">复制的目标点</param>
        /// <returns>复制实体的ObjectId</returns>
        public static ObjectId Copy(this ObjectId id, Point3d sourcePt, Point3d targetPt)
        {
            //构建用于复制实体的矩阵
            var vector = targetPt.GetVectorTo(sourcePt);
            var mt = Matrix3d.Displacement(vector);
            //获取Id所表示的实体对象
            var ent = id.GetObject(OpenMode.ForRead) as Entity;
            //获取实体的复制件
            var entCopy = ent.GetTransformedCopy(mt);
            //将复制的实体对象添加到模型空间
            var copyId = id.Database.AddEntityToModelSpace(entCopy);
            return copyId;  //返回复制实体的ObjectId
        }
        /// <summary>
        /// 旋转 
        /// </summary>
        /// <param name="id">实体ObjectId</param>
        /// <param name="basePt">旋转基点</param>
        /// <param name="angle">旋转角度</param>
        public static void Rotate(this ObjectId id, Point3d basePt, double angle)
        {
            var mt = Matrix3d.Rotation(angle, Vector3d.ZAxis, basePt);
            var ent = id.GetObject(OpenMode.ForWrite) as Entity;
            ent.TransformBy(mt);
        }
        /// <summary>
        /// 缩放
        /// </summary>
        /// <param name="id">实体的ObjectId</param>
        /// <param name="basePt">缩放基点</param>
        /// <param name="scaleFactor">缩放比例</param>
        public static void Scale(this ObjectId id, Point3d basePt, double scaleFactor)
        {
            var mt = Matrix3d.Scaling(scaleFactor, basePt);
            var ent = id.GetObject(OpenMode.ForWrite) as Entity;
            ent.TransformBy(mt);
        }
        /// <summary>
        /// 镜像
        /// </summary>
        /// <param name="id">实体ObjectId</param>
        /// <param name="mirrorPt1">镜像点1</param>
        /// <param name="mirrorPt2">镜像点2</param>
        /// <param name="eraseSourceObject">是否删除源对象</param>
        /// <returns>镜像实体的ObjectId</returns>
        public static ObjectId Mirror(this ObjectId id, Point3d mirrorPt1, Point3d mirrorPt2, bool eraseSourceObject)
        {
            var miLine = new Line3d(mirrorPt1, mirrorPt2);  // 镜像线
            var mt = Matrix3d.Mirroring(miLine);  //镜像矩阵
            ObjectId mirrorId = id;
            var ent = id.GetObject(OpenMode.ForWrite) as Entity;
            //如果删除源对象，则直接对源对象实行镜像变换
            if (eraseSourceObject == true)
            {
                ent.TransformBy(mt);
            }
            else  //如果不删除源对象，则镜像复制源对象
            {
                var entCopy = ent.GetTransformedCopy(mt);
                mirrorId = id.Database.AddEntityToModelSpace(entCopy);
            }
            return mirrorId;
        }
        /// <summary>
        /// 添加到图形数据库
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="entity"></param>
        /// <param name="host"></param>
        /// <param name="btr"></param>
        /// <returns></returns>
        public static ObjectId AddToDatabase<TEntity>(this TEntity entity, Transaction host, BlockTableRecord btr)
where TEntity : Entity
        {
            if (entity is null) return ObjectId.Null;
            var id = btr.AppendEntity(entity);
            host.AddNewlyCreatedDBObject(entity, true);
            return id;
        }
        /// <summary>
        /// 添加到图形数据库
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="entities"></param>
        /// <param name="host"></param>
        /// <param name="model_space"></param>
        /// <returns></returns>
        public static List<ObjectId> AddToDatabase<TEntity>(this ICollection<TEntity> entities, Transaction host, BlockTableRecord model_space)
            where TEntity : Entity
        {
            return entities.Select(entity =>
            {
                var id = model_space.AppendEntity(entity);
                host.AddNewlyCreatedDBObject(entity, true);
                return id;
            }).ToList();
        }
        /// <summary>
        /// 添加到图形数据库
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="trans"></param>
        /// <param name="btr"></param>
        /// <param name="entities"></param>
        /// <returns></returns>
        public static List<ObjectId> AddEntityToDatabase<TEntity>(this Transaction trans, BlockTableRecord btr, params TEntity[] entities)
where TEntity : Entity
        {
            return entities.Select(entity =>
            {
                var id = btr.AppendEntity(entity);
                trans.AddNewlyCreatedDBObject(entity, true);
                return id;
            }).ToList();
        }

        /// <summary>
        /// 将图形对象添加到图形文件中
        /// </summary>
        /// <param name="db">图形数据库</param>
        /// <param name="entity">图形对象</param>
        /// <returns>图形的objectid</returns>
        public static ObjectId AddEntityToModelSpace(this Database db, Entity entity)
        {
            //声明ObjectId，用于返回
            ObjectId entityId = ObjectId.Null;
            //开启事务
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                //打开块表
                BlockTable bt = (BlockTable)trans.GetObject(db.BlockTableId, OpenMode.ForRead);
                //打开块表记录
                BlockTableRecord btr = (BlockTableRecord)trans.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
                //添加图形到块表记录
                entityId = btr.AppendEntity(entity);
                //更新数据信息
                trans.AddNewlyCreatedDBObject(entity, true);
                //提交
                trans.Commit();
            }
            return entityId;
        }
        /// <summary>
        /// 将图形对象添加到图形文件中
        /// </summary>
        /// <param name="db">图形数据库</param>
        /// <param name="entity">图形对象，可变参数</param>
        /// <returns>图形的objectId，数组返回</returns>
        public static ObjectId[] AddEntityToModelSpace(this Database db, params Entity[] entity)
        {
            //声明ObjectId 用于返回
            ObjectId[] entityId = new ObjectId[entity.Length];
            //开启事务
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                //打开块表
                BlockTable bt = (BlockTable)trans.GetObject(db.BlockTableId, OpenMode.ForRead);
                // 打开块表记录
                BlockTableRecord btr = (BlockTableRecord)trans.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);

                for (int i = 0; i < entity.Length; i++)
                {
                    //添加图形到块表记录
                    entityId[i] = btr.AppendEntity(entity[i]);
                    //更新数据消息
                    trans.AddNewlyCreatedDBObject(entity[i], true);
                }
                trans.Commit();
            }
            return entityId;
        }


        /// <summary>
        /// 选择
        /// </summary>
        /// <param name="ed"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        public static List<ObjectId> SelectAll_(this Editor ed, SelectionFilter filter = default)
        {
            if (ed is null) return new List<ObjectId>();
            var value = ed.SelectAll(filter).Value;
            if (value is null) return new List<ObjectId>();
            return value.GetObjectIds().ToList();
        }
        /// <summary>
        /// 选择点
        /// </summary>
        /// <param name="ed"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public static Point3d GetPoint_(this Editor ed, string message)
        {
            if (ed is null) return Point3d.Origin;
            var opotion = new PromptPointOptions(message);
            return ed.GetPoint(opotion).Value;
        }
        /// <summary>
        /// 选择（通过图层过滤）
        /// </summary>
        /// <param name="layerNames"></param>
        /// <returns></returns>
        public static SelectionFilter GetSelectionFilterByLayerName(params string[] layerNames)
        {
            var start = new TypedValue((int)DxfCode.Operator, "<or");
            var end = new TypedValue((int)DxfCode.Operator, "or>");

            var layerTypeValues = layerNames.Select(x => new TypedValue((int)DxfCode.LayerName, x)).ToList();
            layerTypeValues.Insert(0, start);
            layerTypeValues.Add(end);
            return new SelectionFilter(layerTypeValues.ToArray());
        }

        /// <summary>
        /// 删除图形对象
        /// </summary>
        /// <param name="entId">图形对象的ObjectId</param>
        public static void EraseEntity(this ObjectId entId)
        {
            //打开事务处理
            using (Transaction trans = entId.Database.TransactionManager.StartTransaction())
            {
                //打开要删除的图形对象
                Entity ent = (Entity)entId.GetObject(OpenMode.ForWrite);
                //删除
                ent.Erase();
                trans.Commit();
            }
        }

        /// <summary>
        /// 通过ObjectId得到实体
        /// </summary>
        /// <param name="objIds"></param>
        /// <param name="db"></param>
        /// <returns></returns>
        public static Entity[] GetEntity(this ObjectId[] objIds, Database db)
        {

            Entity[] ents = new Entity[objIds.Length];
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                for (int i = 0; i < objIds.Length; i++)
                {
                    DBObject dBObj = objIds[i].GetObject(OpenMode.ForRead);
                    ents[i] = (Entity)dBObj;
                }
            }
            return ents;
        }
        /// <summary>
        /// 通过ObjectId得到实体
        /// </summary>
        /// <param name="objId"></param>
        /// <param name="db"></param>
        /// <returns></returns>
        public static Entity GetEntity(this ObjectId objId, Database db)
        {

            Entity ent = null;
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {

                DBObject dBObj = objId.GetObject(OpenMode.ForRead);
                ent = (Entity)dBObj;

            }
            return ent;
        }


        /// <summary>
        /// 通过圆心、起点夹角绘制圆弧
        /// </summary>
        /// <param name="db">图形数据库</param>
        /// <param name="center">圆心</param>
        /// <param name="startPoint">起点</param>
        /// <param name="degree">夹角,角度值</param>
        /// <returns>ObjectId</returns>
        public static ObjectId AddArcToModelSpace(this Database db, Point3d center, Point3d startPoint, double degree)
        {
            //获取半径
            double radius = center.DistanceTo(startPoint);
            //获取起点角度
            double startAngle = center.GetAngleToXAxis(startPoint);
            //声明圆弧对象
            Arc arc = new Arc(center, radius, startAngle, startAngle + degree.DegreeToAngle());
            return db.AddEntityToModelSpace(arc);
        }
        /// <summary>
        /// 绘制圆弧
        /// </summary>
        /// <param name="db">图形数据库</param>
        /// <param name="startPoint">圆弧的起始点</param>
        /// <param name="pointOnArc"></param>
        /// <param name="endPoint">圆弧的终止点</param>
        /// <returns>ObjectId</returns>
        public static ObjectId AddArcToModelSpace(this Database db, Point3d startPoint, Point3d pointOnArc, Point3d endPoint)
        {
            //创建几何类对象
            CircularArc3d cArc = new CircularArc3d(startPoint, pointOnArc, endPoint);
            //先判断三点是否在同一条直线上
            if (startPoint.IsOnOneLine(pointOnArc, endPoint))
            {
                return ObjectId.Null;
            }
            //创建圆弧对象
            Arc arc = new Arc(cArc.Center, cArc.Radius, cArc.Center.GetAngleToXAxis(startPoint), cArc.Center.GetAngleToXAxis(endPoint));
            // 加入图形数据库
            return db.AddEntityToModelSpace(arc);
        }

        /// <summary>
        /// 绘制圆弧
        /// </summary>
        /// <param name="db">图形数据库</param>
        /// <param name="center">圆弧所在圆的圆心点</param>
        /// <param name="radius">圆弧的半径</param>
        /// <param name="startDegree">圆弧的起始角度</param>
        /// <param name="endDegree">圆弧的终止角度</param>
        /// <returns>ObjectId</returns>
        public static ObjectId AddArcToModelSpace(this Database db, Point3d center, double radius, double startDegree, double endDegree)
        {
            return db.AddEntityToModelSpace(new Arc(center, radius, startDegree.DegreeToAngle(), endDegree.DegreeToAngle()));
        }
        /// <summary>
        /// 绘制直线 
        /// </summary>
        /// <param name="db">图形数据库</param>
        /// <param name="startPoint">起点坐标</param>
        /// <param name="length">直线长度</param>
        /// <param name="degree">与x轴正方向的角度</param>
        /// <returns>ObjectId</returns>
        public static ObjectId AddLineToModelSpace(this Database db, Point3d startPoint, double length, double degree)
        {
            //计算终点坐标
            double x = startPoint.X + length * Math.Cos(degree.DegreeToAngle());
            double y = startPoint.Y + length * Math.Sin(degree.DegreeToAngle());
            Point3d endPoint = new Point3d(x, y, 0);
            return db.AddEntityToModelSpace(new Line(startPoint, endPoint));
        }
    }
}
